package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"reflect"
	"strings"
	"swagger-to-ts/types"
	"swagger-to-ts/utils"
)

const BASE_DIR = "out"

func main() {
	// data, err := OpenLocalJsonFile()
	fmt.Println("请输入 swagger.json 文件的URL，例如：http://localhost:5801/swagger/v1/swagger.json")
	fmt.Printf("请输入 :")
	var url string = "http://localhost:5801/swagger/v1/swagger.json"
	fmt.Scanln(&url)

	data, err := GetHttpJson(url)

	if !err {
		return
	}
	// 解析 components
	// fmt.Println(ParseComponents(data))
	// 解析 paths
	ParsePaths(data)
	// 等待用户输入
	fmt.Println("请按任意键退出...")
	var input string
	fmt.Scanln(&input)
}

func GetHttpJson(url string) (interface{}, bool) {
	if url == "" {
		url = "http://localhost:5801/swagger/v1/swagger.json"
	}
	res, err := http.Get(url)
	fmt.Println(res.StatusCode)
	if err != nil {
		fmt.Println("获取 swagger.json 文件失败，请检查文件是否存在", err)
		return nil, false
	}
	defer res.Body.Close()

	if res.StatusCode != 200 {
		fmt.Println("获取 swagger.json 文件失败，请检查后端服务是否启动", res.StatusCode)
		return nil, false

	}

	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		fmt.Println("获取 swagger.json 文件解析失败", err)
		return nil, false
	}

	var data interface{}
	json.Unmarshal([]byte(string(body)), &data)

	if err != nil {
		fmt.Println("JSON 解析失败", err)
		return nil, false
	}

	// 先转义成map才能访问属性
	jsonmap, _ := data.(map[string]interface{})

	return jsonmap, true
}

// 读取本地 swagger.json 文件
func OpenLocalJsonFile() (interface{}, bool) {
	file, err := os.Open("swagger.json")
	// file, err := os.Open("示例项目.openapi.json")

	if err != nil {
		fmt.Println("获取 swagger.json 文件失败，请检查文件是否存在", err)
		return nil, false
	}

	defer file.Close()

	bytes, err := ioutil.ReadAll(file)

	if err != nil {
		fmt.Println("获取 swagger.json 文件解析失败", err)
		return nil, false
	}

	var data interface{}
	json.Unmarshal([]byte(string(bytes)), &data)

	if err != nil {
		fmt.Println("JSON 解析失败", err)
		return nil, false
	}

	// 先转义成map才能访问属性
	jsonmap, _ := data.(map[string]interface{})

	return jsonmap, true
}

// 解析components
func ParseComponents(data interface{}) map[string]interface{} {
	componentsMap := make(map[string]interface{})
	// 获取 components 中的 schemas
	components, ok := data.(map[string]interface{})["components"].(map[string]interface{})
	if !ok {
		fmt.Println("数据不是 map[string]interface{} 类型")
		return nil
	}

	// 获取 schemas
	schemas, ok := components["schemas"].(map[string]interface{})
	if !ok {
		fmt.Println("schemas 不是 []interface{} 类型")
		return nil
	}

	// 遍历 schemas
	for key, value := range schemas {
		// fmt.Println(key, value)
		componentsMap[key] = value
	}

	return componentsMap
}

// 解析paths
func ParsePaths(data interface{}) map[string]interface{} {
	// 删除目录
	os.RemoveAll(BASE_DIR)
	pathsMap := make(map[string]interface{})
	components := ParseComponents(data)
	// 获取 paths
	paths, ok := data.(map[string]interface{})["paths"].(map[string]interface{})
	if !ok {
		fmt.Println("paths 不是 []interface{} 类型")
		return nil
	}
	// 遍历 paths
	for key, value := range paths {
		fmt.Println("处理API：", key)
		pathObj := types.PathMap{}
		// 文件内容
		contextText := ""

		for valueKey, valueValue := range value.(map[string]interface{}) {
			// fmt.Println(valueKey, valueValue)
			// 请求方法类型
			pathObj.Method = strings.ToUpper(valueKey)
			// 请求路径
			pathObj.Path = key

			pathObj.Tags = valueValue.(map[string]interface{})["tags"].([]interface{})[0].(string)

			// 提取最后一个 / 后面的字符串用于变量名声明
			keyStrList := strings.Split(key, "/")
			feildName := keyStrList[len(keyStrList)-1]

			// 解析获取请求参数-parmas
			if valueValue.(map[string]interface{})["parameters"] != nil {
				pathObj.Request.Parmas = strings.Join(ParseRequestObject(valueValue.(map[string]interface{})["parameters"], feildName), "\n")
				// fmt.Println(pathObj.Request.Parmas)
				contextText += "// =============Parmas===========\n"
				contextText += pathObj.Request.Parmas
			}

			// 解析获取请求参数-body
			if valueValue.(map[string]interface{})["requestBody"] != nil {
				pathObj.Request.Data = ParseRequestBody(valueValue.(map[string]interface{})["requestBody"], feildName, components)
				contextText += "//=============请求体Body===========\n"
				contextText += pathObj.Request.Data
			}

			// 响应体
			if valueValue.(map[string]interface{})["responses"] != nil {
				responses := valueValue.(map[string]interface{})["responses"].(map[string]interface{})["200"].(map[string]interface{})["content"]
				if responses != nil {
					pathObj.Response = ParseResponseObject(responses, feildName, components)

				}

				contextText += "\n//++++++++++++++++++++++++++\n"
				contextText += "//++++++++++++ 响应体 +++++++++\n"
				contextText += "//++++++++++++++++++++++++++\n"
				contextText += pathObj.Response

			}
		}

		// 生成文件
		filePath := BASE_DIR + pathObj.Path + "（" + pathObj.Method + "）.ts"
		utils.Mkdirs(filePath)
		err := ioutil.WriteFile(filePath, []byte(contextText), 0644)
		if err != nil {
			fmt.Println("无法写入文件:", err)

		}
		pathsMap[key] = pathObj
	}

	return pathsMap
}

// 处理响应体
func ParseResponseObject(data interface{}, feildName string, components map[string]interface{}) string {

	for _, value := range data.(map[string]interface{}) {
		// fmt.Println(value)
		if value.(map[string]interface{})["schema"] == nil {
			return ""
		}
		ref := value.(map[string]interface{})["schema"].(map[string]interface{})["$ref"]
		if ref != nil {
			return strings.Join(ParseRef(ref.(string), feildName, components, 1), "\n")
		}
		return ""
	}
	return ""
}

// 解析获取请求参数-parmas
func ParseRequestObject(data interface{}, feildName string) []string {
	// "" + feildName + "QueryPInput"
	// fmt.Println("data==", data)
	feildStrList := []string{}
	feildStrList = append(feildStrList, fmt.Sprintf("interface %sQueryPInput {\n", feildName))
	for _, value := range data.([]interface{}) {
		// fmt.Println(value)

		// 生产注释
		if value.(map[string]interface{})["description"] != nil &&
			value.(map[string]interface{})["description"].(string) != "" {
			feildStrList[0] += "\t// " + value.(map[string]interface{})["description"].(string) + "\n"
		}

		// 生成字段
		dataType := types.CdDataTyoe[value.(map[string]interface{})["schema"].(map[string]interface{})["type"].(string)]
		if dataType == "" {
			dataType = "any"
		}

		// 字符按名
		feildNameStr := value.(map[string]interface{})["name"].(string)
		// 是必填
		mustStr := "?: "
		if feildNameStr == "Page" || feildNameStr == "PageSize" {
			mustStr = ": "
		}

		feildStrList[0] += "\t" + feildNameStr + mustStr + dataType + ";\n"
	}

	feildStrList[0] += "} \n"

	// fmt.Println(feildStr)
	return feildStrList
}

// 解析获取请求参数-body
func ParseRequestBody(data interface{}, feildName string, components map[string]interface{}) string {
	feildStrList := []string{}

	for _, value := range data.(map[string]interface{})["content"].(map[string]interface{}) {

		schema := value.(map[string]interface{})["schema"].(map[string]interface{})
		feildName += "QueryInput"
		if schema["$ref"] != nil {
			feildStrList = append(feildStrList, ParseRef(schema["$ref"].(string), feildName, components, 1)...)
			// 是否有组件
		} else if schema["type"] != nil {
			// 没有组件

			if schema["type"].(string) == "object" {
				// 参数是对象类型
				required := []string{}
				if schema["required"] != nil {
					// []interface 转[]string
					for _, v := range schema["required"].([]interface{}) {
						required = append(required, v.(string))
					}
				}

				feildStrList = ParseRequestBodyObject(schema["properties"], feildName, required)
			} else if schema["type"].(string) == "array" {
				// 参数是数组类型
				items := schema["items"].(map[string]interface{})

				dataType := types.CdDataTyoe[schema["type"].(string)]
				properties := items
				if items["properties"] != nil {
					properties = items["properties"].(map[string]interface{})
				}
				// fmt.Println("参数是数组类型", properties)
				if dataType == "object" {
					required := []string{}
					dataType = strings.Title(feildName)
					feildStrList = append(feildStrList, ParseRequestBodyObject(properties, feildName, required)...)
				} else if dataType == "array" {

					dataType = strings.Title(feildName) + "[]"
					if properties["$ref"] != nil {
						feildStrList = append(feildStrList, ParseRef(properties["$ref"].(string), feildName, components, 1)...)
					} else {

						if reflect.TypeOf(properties).Kind() == reflect.Map {
							// someInterface 是 map 类型
							if items["type"] != nil {
								newDataType := types.CdDataTyoe[items["type"].(string)]
								feildStrList = append(feildStrList, "// 参数数据类型为数组\n"+newDataType+"[]\n")
							}

						} else {
							// someInterface 不是 map 类型
							feildStrList = append(feildStrList, ParseRequestBodyObject(properties, feildName, []string{})...)

						}
					}
				} else {
					// 参数是基本类型
					dataType += "[]"
					feildStrList = append(feildStrList, fmt.Sprintf("type %sQueryInput %s", feildName, dataType))
				}

			} else {
				// 参数是基本类型
				dataType := types.CdDataTyoe[schema["type"].(string)]
				if dataType == "" {
					dataType = "any"
				}
				feildStrList = append(feildStrList, "// 参数数据类型为基础类型 "+dataType+"\n")
			}

		} else {
			fmt.Println("未知类型", value)
		}

		break
	}

	// fmt.Println(feildStrList)

	return strings.Join(feildStrList, "\n")
}

func ParseRequestBodyObject(data interface{}, feildName string, required []string) []string {

	feildStrList := []string{}

	feildStrList = append(feildStrList, fmt.Sprintf("interface %s {\n", strings.Title(feildName)))
	for feildNameStr, val := range data.(map[string]interface{}) {
		// fmt.Println(value)
		value := val.(map[string]interface{})
		// 生产注释
		if value["description"] != nil &&
			value["description"].(string) != "" {
			feildStrList[0] += "\t// " + value["description"].(string) + "\n"
		}

		// 生成字段
		dataType := types.CdDataTyoe[value["type"].(string)]
		if dataType == "" {
			dataType = "any"
		}

		if dataType == "object" {
			feildStrList = append(feildStrList, ParseRequestBodyObject(value["properties"], feildNameStr, required)...)
			// 首个字母大写
			dataType = strings.Title(feildNameStr)
		} else if dataType == "array" {
			// 参数是数组类型
			items := value["items"].(map[string]interface{})

			dataType = types.CdDataTyoe[items["type"].(string)]
			if dataType == "object" {
				feildStrList = append(feildStrList, ParseRequestBodyObject(items["properties"], feildNameStr, required)...)
				dataType = strings.Title(feildNameStr)
			}

			dataType = dataType + "[]"
		}

		// 是必填
		mustStr := "?: "
		if required != nil && utils.BaseFind(required, feildNameStr) {
			mustStr = ": "
		}

		feildStrList[0] += "\t" + feildNameStr + mustStr + dataType + ";\n"
	}

	feildStrList[0] += "} \n"

	return feildStrList
}

func ParseRef(refKey string, feildName string, components map[string]interface{}, index int) []string {
	if index > 5 {
		return []string{}
	}
	feildStrList := []string{}

	refKeyList := strings.Split(refKey, "/")
	refKey = refKeyList[len(refKeyList)-1]
	list := components[refKey].(map[string]interface{})
	if list["properties"] != nil {
		feildStrList = append(feildStrList, fmt.Sprintf("interface %s {\n", feildName))
		for feildNameStr, val := range list["properties"].(map[string]interface{}) {
			value := val.(map[string]interface{})

			// 是否必填/可空
			mustStr := ": "
			if value["nullable"] == true {
				mustStr = "?: "
			}
			// 生产注释
			if value["description"] != nil &&
				value["description"].(string) != "" {
				feildStrList[0] += "\t// " + value["description"].(string) + "\n"
			}
			// 字段类型

			if ref, ok := value["$ref"]; ok {
				feildStrList = append(feildStrList, ParseRef(ref.(string), strings.Title(feildNameStr), components, index+1)...)
			} else {
				dataType := "any"

				if value["type"] != nil {
					dataType = types.CdDataTyoe[value["type"].(string)]
				}

				if dataType == "object" {
					className := strings.Title(feildNameStr)
					feildStrList[0] += "\t" + feildNameStr + mustStr + className + ";\n"
					feildStrList = append(feildStrList, ParseRef(ref.(string), className, components, index+1)...)
				} else if dataType == "array" {

					if ref != nil {
						className := strings.Title(feildNameStr)
						feildStrList[0] += "\t" + feildNameStr + mustStr + className + "[];\n"
						feildStrList = append(feildStrList, ParseRef(ref.(string), className, components, index+1)...)
					} else {
						// 基础数据类型
						// log.Println("基础数据类型", value)
						items := value["items"].(map[string]interface{})
						if condition, ok := items["$ref"]; ok {
							className := strings.Title(feildNameStr)
							feildStrList[0] += "\t" + feildNameStr + mustStr + className + "[];\n"
							feildStrList = append(feildStrList, ParseRef(condition.(string), className, components, index+1)...)

						} else {
							itemsDataType := types.CdDataTyoe[items["type"].(string)]
							if itemsDataType == "" {
								itemsDataType = "any"
							}
							feildStrList[0] += "\t" + feildNameStr + mustStr + itemsDataType + "[];\n"
						}

					}

				} else {
					// 字段类型
					dataType := "any"

					if value["type"] != nil {
						dataType = types.CdDataTyoe[value["type"].(string)]
					}

					feildStrList[0] += "\t" + feildNameStr + mustStr + dataType + ";\n"
				}
			}

		}

		feildStrList[0] += "} \n"

	}
	// fmt.Println(feildStrList)
	return feildStrList
}
